#!/bin/env python

"""
Google wave robot for the Howl web service for the Internet of Things.
Uses Dale Lane's Python script from: http://dalelane.co.uk/blog/?p=303

Copyright (C) Sarah Mount, 2009.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have rceeived a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from waveapi import events
from waveapi import model
from waveapi import robot

import urllib
import urllib2
import cookielib
import re

__author__ = 'Sarah Mount'
__credits__ = 'Sam Wilson and Dale Lane http://dalelane.co.uk'
__date__ = 'December 2009'


welcome = """Hello, I am the Howl robot v0.1.

Example Howl commands:
    $$create FeedName temp Celsius humidity percent user=foo@bar.com pass=flibble$$
    $$register FeeName DataProvider 52.586 -2.115 user=foo@bar.com pass=flibble$$
    $$insert FeedName ProviderName temp 24.5 user=foo@bar.com pass=flibble$$
"""

# Define a regular expression for extracting Howl commands, usernames
# and passwords from a Google Wave blip.
howl_re = re.compile(r'\$\$(?P<command>(\S+\s)+?)user\=(?P<user>\S+) pass\=(?P<pass>\S+)\$\$')

# User data
baseurl = 'http://uowapplication.appspot.com/'
target_authenticated_google_app_engine_uri = baseurl + 'PostCommand'
my_app_name = "uowapplication"


def authenticate(users_email_address, users_password):
    """Authenticate a Google service user and return an authentication token.
    """

    # We use a cookie to authenticate with Google App Engine
    # by registering a cookie handler here, this will automatically store the
    # cookie returned when we use urllib2 to open
    # http://uowapplication.appspot.com/_ah/login
    cookiejar = cookielib.LWPCookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
    urllib2.install_opener(opener)

    # Get an AuthToken from Google accounts
    auth_uri = 'https://www.google.com/accounts/ClientLogin'
    authreq_data = urllib.urlencode({ "Email":   users_email_address,
                                      "Passwd":  users_password,
                                      "service": "ah",
                                      "source":  my_app_name,
                                      "accountType": "HOSTED_OR_GOOGLE" })
    auth_req = urllib2.Request(auth_uri, data=authreq_data)
    auth_resp = urllib2.urlopen(auth_req)
    auth_resp_body = auth_resp.read()
    # auth response includes several fields - we're interested in
    #  the bit after Auth=
    auth_resp_dict = dict(x.split("=")
                          for x in auth_resp_body.split("\n") if x)
    authtoken = auth_resp_dict["Auth"]
    return authtoken


def run_command(email, password, command):
    """Run a Howl command with given user authentication.
    """
    authtoken = authenticate(email, password)

    # this is where I actually want to go to
    serv_uri = (target_authenticated_google_app_engine_uri +
                "?command=" + command)

    serv_args = {'continue':serv_uri, 'auth':authtoken}

    full_serv_uri = baseurl + "_ah/login?%s" % (urllib.urlencode(serv_args))

    serv_req = urllib2.Request(full_serv_uri)
    serv_resp = urllib2.urlopen(serv_req)
    serv_resp_body = serv_resp.read()
    return


def run_howl_command(email, password, command):
    """(Safely) run a single howl command.

    Return text of any error messages, or empty string.
    """
    try:
        run_command(email, password, command)
        return ''
    except BaseException, exn:
        return str(type(exn)) + '\n' + str(exn.args)


def OnRobotAdded(properties, context):
    """Invoked when the robot has been added."""
    HowlBlip(context, welcome)
    return


def HowlBlip(context, text):
    """
- style/backgroundColor
- style/color
- style/fontFamily
- style/fontSize
- style/fontStyle
- style/fontWeight
- style/textDecoration
- style/verticalAlign
    """
    root_wavelet = context.GetRootWavelet()
    blip = root_wavelet.CreateBlip()
    doc = blip.GetDocument()
    doc.AnnotateDocument('style/backgroundColor', 'rgb(0, 0, 0)')
    doc.AnnotateDocument('style/color', 'rgb(0, 255, 0)')
    doc.AnnotateDocument('style/fontWeight', 'bold')
    doc.AnnotateDocument('style/fontFamily', 'monospace')
    doc.SetText(text)
    return


def OnBlipSubmitted(properties, context):
    """Invoked when a new blip has been submitted.
    """
    blip = context.GetBlipById(properties['blipId']).GetDocument()
    txt = blip.GetText()
    howlcommands = howl_re.finditer(txt)
    root_wavelet = context.GetRootWavelet()
    # Process Howl commands.
    errors = []
    responses = []
    for howlcommand in howlcommands:
        command = howlcommand.group('command')
        username = howlcommand.group('user')
        password = howlcommand.group('pass')
        howl_response = run_howl_command(username, password, command)
        if howl_response == '':
            responses.append("Successfully executed Howl command: %s"
                             % command)
        else:
            errors.append('Howl ERROR: ' + howl_response)
    # Append a new blip with results of Howl operations.
    if len(responses) + len(errors) == 0:
        return
    bliptxt = ''
    for response in responses:
        bliptxt += response + '\n'
    for error in errors:
        bliptxt += error + '\n'
    HowlBlip(context, bliptxt)
    return


if __name__ == '__main__':
    howlbot = robot.Robot('howl-robot', 
                          image_url='http://howl-robot.appspot.com/howl.png',
                          version='1',
                          profile_url='http://howl-robot.appspot.com/')
    howlbot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
    howlbot.RegisterHandler(events.BLIP_SUBMITTED, OnBlipSubmitted)
    howlbot.RegisterHandler(events.WAVELET_BLIP_CREATED, OnBlipSubmitted)
    howlbot.Run(debug=True)
